/**
 * 
 */
package edu.arizona.bio5.onto.modularity;

import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.util.Collections;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;

import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLEntity;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyCreationException;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.modularity.OntologySegmenter;
import org.semanticweb.owlapi.util.DLExpressivityChecker;

import uk.ac.manchester.cs.owlapi.modularity.ModuleType;
import uk.ac.manchester.cs.owlapi.modularity.SyntacticLocalityModuleExtractor;
import uk.ac.manchester.cs.util.SetUtils;
import edu.arizona.bio5.onto.decomposition.ADProcessingTaskRunner;
import edu.arizona.bio5.onto.decomposition.AtomicDecomposition;
import edu.arizona.bio5.onto.decomposition.persistent.ADPersistenceException;
import edu.arizona.bio5.onto.decomposition.persistent.PersistentAtomicDecomposition;

/**
 * @author Pavel Klinov
 *
 */
public class FMERunner implements ADProcessingTaskRunner {

	static final ModuleType MODULE_TYPE = ModuleType.BOT;
	private final SeedSigBasedModuleExtractor m_extractor = new SeedSigBasedModuleExtractor();
	private File m_ontFile = null;
	
	public void setOntologyFile(File file) {
		
		m_ontFile = file;
	}
	
	/* 
	 * 
	 */
	@Override
	public void process(PersistentAtomicDecomposition persAD, Writer csvWriter) throws IOException {
		
		OWLOntologyManager mgr = OWLManager.createOWLOntologyManager();
		OWLOntology ontology;
		
		//AtomicDecomposition ad = (AtomicDecomposition)persAD;
		
		csvWriter.write(m_ontFile.getName() + ",");
		
		try {
			ontology = mgr.loadOntologyFromOntologyDocument(m_ontFile);
			
			DLExpressivityChecker exprChecker = new DLExpressivityChecker(Collections.singleton(ontology));
			
			csvWriter.write(exprChecker.getDescriptionLogicName() + ",");
			persAD.loadEverything();
			
		} catch (OWLOntologyCreationException e) {

			throw new RuntimeException("Signature can't be generated");
		} catch (ADPersistenceException e) {
			throw new RuntimeException("Axioms can't be loaded");
		}
		
		//csvWriter.write(ad.stats().toCommaSeparatedString() + ",");
		//Generate random signatures of size 2-10
		for (int sigSize = 2; sigSize <= 10; sigSize += 2) {
			
			System.out.println("FME evaluation, seed signatures of size " + sigSize);
			
			Random rnd = new Random();
			double redRatio = 0d;
			int atomsPerModule = 0;
			int axPerModule = 0;
			final int REPEATS = 100;
			long totalFMEtime = 0;
			long totalMEtime = 0;
			//int totalSeedSigSize = 0;
			
			for (int i = 0; i < REPEATS; i++) {
				
				Set<OWLEntity> rndSig = generateRandomQuerySignature((AtomicDecomposition)persAD, ontology, sigSize, rnd);
				ModuleStats mStats = testModuleExtraction((AtomicDecomposition)persAD, rndSig, ontology);
				
				//totalSeedSigSize += rndSig.size();
				redRatio += mStats.mRedundancy;
				atomsPerModule += mStats.mAtomsPerModule;
				axPerModule += mStats.mAxiomsPerModule;
				totalFMEtime += mStats.mADTime;
				totalMEtime += mStats.mStandardTime;
				
				/*try {
					ad.unloadEverything();
				} catch (ADPersistenceException e) {
					throw new RuntimeException("Unloading axioms failed", e);
				}*/
			}
			
			csvWriter.write((1d*redRatio*100)/REPEATS + "," + (1d*atomsPerModule)/REPEATS + "," +
					(1d*axPerModule)/REPEATS + "," + (1d * totalFMEtime / (REPEATS * 1000000)) + "," + (1d * totalMEtime / (REPEATS * 1000000)) + ",");
			
			//System.out.println("Avegare seed signature size: " + 1d * totalSeedSigSize / REPEATS);
			System.out.println("Average relative speed-up: " + ((1d * totalMEtime) / totalFMEtime));
			System.out.println("FME: " + ((1d * totalFMEtime) / (REPEATS * 1000000)));
			System.out.println("ME: " + ((1d * totalMEtime) / (REPEATS * 1000000)));
		}
	}
	
	private Set<OWLEntity> generateRandomQuerySignature(AtomicDecomposition ad, OWLOntology ontology, int sigSize, Random rnd) {

		Set<OWLEntity> rndSig = new HashSet<OWLEntity>(sigSize);
		Set<OWLClass> classes = ontology.getClassesInSignature(false);

		rndSig.addAll(SetUtils.pickRandomSubset(classes, sigSize, rnd));
		
		return rndSig;
		
		/*Set<OWLEntity> rndSig = new HashSet<OWLEntity>(sigSize);
		Set<OWLClass> classes = SetUtils.pickRandomSubset(ontology.getClassesInSignature(false), sigSize, rnd);

		rndSig.addAll(classes);
		
		for (OWLAxiom axiom : ontology.getAxioms()) {
			
			if (axiom.isLogicalAxiom()) {
				
				if (!SetUtils.intersection(axiom.getClassesInSignature(), classes).isEmpty()) {
					rndSig.addAll(axiom.getClassesInSignature());
				}
			}
		}
		
		return rndSig;*/		
	}


	protected ModuleStats testModuleExtraction(AtomicDecomposition ad, Set<OWLEntity> rndSig, OWLOntology ontology) throws IOException {
		//Extract the LAD-based module
		m_extractor.setDecomposition(ad);
		Signature copySig = new Signature(rndSig);
		long adTime =  System.nanoTime();
		Set<OWLAxiom> adModule = m_extractor.extract(copySig, false);
		
		adTime = System.nanoTime() -  adTime;
		//Extract the standard module
		OWLOntologyManager mgr = OWLManager.createOWLOntologyManager();

		/*try {
			ontology = mgr.loadOntologyFromOntologyDocument(m_ontFile);
		} catch (OWLOntologyCreationException e) {

			throw new IOException(e.getMessage());
		}*/

		OntologySegmenter extractor = new SyntacticLocalityModuleExtractor(mgr, ontology, MODULE_TYPE);
		
		long standardTime = System.nanoTime();
		
		Set<OWLAxiom> module = extractor.extract(rndSig);
		
		standardTime = System.nanoTime() - standardTime;
		//Evaluate redundancy
		int redCnt = 0;
		
		for (OWLAxiom axiom : adModule) {
			
			if (!module.contains(axiom)) redCnt++;
		}
		
		double redRatio = !(redCnt == 0d && module.isEmpty()) ? (1d * redCnt) / module.size() : 0d;
		
		return new ModuleStats(redRatio, m_extractor.getRelevantAtomNumber(), adModule.size(), adTime, standardTime); 
	}
}